﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ActivateYourGlutes.Data;
using ActivateYourGlutes.Data.Model;

namespace ActivateYourGlutes.Services.Implementation
{
    public class ProviderBase<T> : IProviderBase where T : class
    {
        private readonly IRepository<T> _repository;
        private readonly IEntityModelFactory _repositoryFactory;

        protected ProviderBase(IEntityModelFactory factory)
        {
            _repository = factory.GetRepository<T>();
             _repositoryFactory = factory;
        }

        protected IRepository<T> Repository
        {
            get { return _repository; }
        }

        protected IRepository<T2> GetRepository<T2>() where T2 : class
        {
            return _repositoryFactory.GetRepository<T2>(Repository);
        }

        #region IProviderBase Members

        #endregion

        protected void TryUpdateModel(object destination, object source, string[] properties)
        {
            if (destination == null)
                throw new ArgumentNullException("destination");
            if (source == null)
                throw new ArgumentNullException("source");
            if (properties == null || properties.Length == 0)
                throw new ArgumentNullException("properties");
            Type destinationType = destination.GetType();
            Type sourceType = source.GetType();

            foreach (string propertyName in properties)
            {
                PropertyInfo destinationProperty = destinationType.GetProperty(propertyName);
                PropertyInfo sourceProperty = sourceType.GetProperty(propertyName);

                if (destinationProperty == null || sourceProperty == null)
                    throw new ModelException("Source and/or destination properties do not exist");

                if (destinationProperty.PropertyType != sourceProperty.PropertyType)
                    throw new ModelException(
                        "Properties exist on both source and destination but are of differing types");

                destinationProperty.SetValue(destination, sourceProperty.GetValue(source, null), null);
            }
        }

        /// <summary>
        /// Returns a list of the measurement types that can be logged against
        /// </summary>
        protected List<MeasurementType> GetMeasurementTypes(Services.Model.Account forAccount)
        {
            var measurementTypeRepository = _repositoryFactory.GetRepository<MeasurementType>(Repository);
            var measurementTypes = measurementTypeRepository.EagerlyLoadedEntities("Owner")
                .Where(
                    mt => mt.Owner == null || mt.Owner.AccountId == forAccount.AccountId)
                .ToList();
            return measurementTypes;
        }
    }
}